package com.woniuxy.movie.product.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.movie.commons.core.model.Result;
import com.woniuxy.movie.commons.web.util.StateData;
import com.woniuxy.movie.product.client.CinemaClient;
import com.woniuxy.movie.product.client.dto.HallDto;
import com.woniuxy.movie.product.dao.ProductMovieMapper;
import com.woniuxy.movie.product.dao.ProductSessionMapper;
import com.woniuxy.movie.product.dto.seat.SeatDto;
import com.woniuxy.movie.product.exception.ProductException;
import com.woniuxy.movie.product.form.session.CreateSessionForm;
import com.woniuxy.movie.product.model.ProductMovie;
import com.woniuxy.movie.product.model.ProductSession;
import com.woniuxy.movie.product.model.ProductTicket;
import com.woniuxy.movie.product.service.ProductMovieService;
import com.woniuxy.movie.product.service.ProductSessionService;
import com.woniuxy.movie.product.service.ProductTicketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 电影场次表 服务实现类
 * </p>
 *
 * @author 刘存益
 * @since 2023年11月17日
 */
@Service
public class ProductSessionServiceImpl extends ServiceImpl<ProductSessionMapper, ProductSession> implements ProductSessionService {

    @Autowired
    private CinemaClient cinemaClient;
    @Autowired
    private ProductMovieMapper productMovieMapper;
    @Autowired
    private ProductTicketService productTicketService;
    @Autowired
    private ProductSessionMapper productSessionMapper;
    @Autowired
    private ProductMovieService productMovieService;

    /**
     * 批量排场次
     * @param forms
     * @return
     */
    @Override
    @Transactional
    public Boolean creates(List<CreateSessionForm> forms) {
        //判断影厅
        Set<Integer> hallIds = forms.stream().map(CreateSessionForm::getHallId).collect(Collectors.toSet());
        Result<List<HallDto>> hallResult = cinemaClient.selHallIds(hallIds);
        if (hallResult.getCode()!=200) {
            throw new ProductException(hallResult.getCode(),hallResult.getMsg());
        }
        List<HallDto> hallDtos = hallResult.getData();
        if (hallDtos.size()!=hallIds.size()) {
            throw new ProductException(2001,"有影厅不存在");
        }
        Map<Integer, HallDto> hallMap = new HashMap<>();
        for (HallDto hallDto : hallDtos) {
            if (hallDto.getHallState()!= StateData.HALL_STATE_NORMAL) {
                throw new ProductException(2002,hallDto.getHallName()+"不可用");
            }
            hallMap.put(hallDto.getHallId(),hallDto);
        }
        //判断电影
        Set<Integer> movieIds = forms.stream().map(CreateSessionForm::getMovieId).collect(Collectors.toSet());
        List<ProductMovie> productMovies = productMovieMapper.selectBatchIds(movieIds);
        if (productMovies==null||movieIds.size()!=productMovies.size()) {
            throw new ProductException(2003,"有电影不存在");
        }
        Map<Integer, ProductMovie> movieMap = new HashMap<>();
        for (ProductMovie productMovie : productMovies) {
            if (productMovie.getMovieState()!= StateData.MOVIE_STATE_NORMAL) {
                throw new ProductException(2004,productMovie.getMovieName()+"不可用");
            }
            movieMap.put(productMovie.getMovieId(),productMovie);
        }
        //判断时间
        List<ProductSession> productSessions = list(new LambdaQueryWrapper<ProductSession>().in(ProductSession::getHallId, hallIds).eq(ProductSession::getSessionState, StateData.SESSION_STATE_NORMAL));
        for (int i = 0; i < forms.size(); i++) {
            LocalDateTime startTime = DateUtil.parseLocalDateTime(forms.get(i).getSessionStartTime());
            LocalDateTime endTime = DateUtil.parseLocalDateTime(forms.get(i).getSessionEndTime());
            if (ChronoUnit.MINUTES.between(startTime,endTime)<=movieMap.get(forms.get(i).getMovieId()).getMovieTime()) {
                throw new ProductException(2005,movieMap.get(forms.get(i).getMovieId()).getMovieName()+":时长不够");
            }
            for (ProductSession productSession : productSessions) {
                if (!Objects.equals(forms.get(i).getHallId(),productSession.getHallId())) {
                    continue;
                }
                LocalDateTime start = DateUtil.parseLocalDateTime(productSession.getSessionStartTime());
                LocalDateTime end = DateUtil.parseLocalDateTime(productSession.getSessionEndTime());
                if (start.isBefore(endTime)&&end.isAfter(startTime)) {
                    throw new ProductException(2006,movieMap.get(forms.get(i).getMovieId()).getMovieName()+":时间冲突");
                }
            }
            //参数自己的时间冲突判定
            for (int j = 0; j < forms.size(); j++) {
                if (i==j) {
                    continue;
                }
                if (!Objects.equals(forms.get(i).getHallId(),forms.get(j).getHallId())) {
                    continue;
                }
                LocalDateTime startTime1 = DateUtil.parseLocalDateTime(forms.get(j).getSessionStartTime());
                LocalDateTime endTime1 = DateUtil.parseLocalDateTime(forms.get(j).getSessionEndTime());
                if (startTime1.isBefore(endTime)&&endTime1.isAfter(startTime)) {
                    throw new ProductException(2007,movieMap.get(forms.get(i).getMovieId()).getMovieName()+":时间冲突");
                }
            }
        }
        //创建场次
        ArrayList<ProductSession> sessions = new ArrayList<>();
        HashMap<Integer, Set<Integer>> sessionSeatMap = new HashMap<>();
        for (CreateSessionForm form : forms) {
            ProductSession productSession = BeanUtil.toBean(form, ProductSession.class);
            productSession.setSessionState(StateData.SESSION_STATE_NORMAL);
            productSession.setHallName(hallMap.get(form.getHallId()).getHallName());
            productSession.setMovieName(movieMap.get(form.getMovieId()).getMovieName());
            productSession.setSessionStartTime(form.getSessionStartTime());
            productSession.setSessionEndTime(form.getSessionEndTime());
            productSession.setMovieTime(movieMap.get(form.getMovieId()).getMovieTime());
            productSession.setMovieImage(movieMap.get(form.getMovieId()).getMovieImage());
            productSessionMapper.addProductSession(productSession);
            sessions.add(productSession);
            sessionSeatMap.put(productSession.getSessionId(),form.getSeatsIds());
        }
        HashMap<Integer, List<SeatDto>> seatMap = new HashMap<>();
        //生成票务
        for (ProductSession session : sessions) {
            List<SeatDto> seatDtos = seatMap.get(session.getHallId());
            //判断是否已查询影厅位置
            if (seatDtos==null||seatDtos.size()==0) {
                Result<List<SeatDto>> seatResult = cinemaClient.selSeatNormal(session.getHallId());
                if (seatResult.getCode()!=200) {
                    throw new ProductException(seatResult.getCode(),seatResult.getMsg());
                }
                seatMap.put(session.getHallId(),seatResult.getData());
                seatDtos = seatResult.getData();
            }
            //判断是否自定义位置
            Set<Integer> createIds = sessionSeatMap.get(session.getSessionId());
            if (createIds!=null&&createIds.size()>0) {
                Set<Integer> seatIds = seatDtos.stream().map(SeatDto::getSeatId).collect(Collectors.toSet());
                if (!seatIds.containsAll(createIds)) {
                    throw new ProductException(2008,"存在不可用的座位号");
                }
                seatDtos = seatDtos.stream().filter(o->createIds.contains(o.getSeatId())).collect(Collectors.toList());
            }
            //创建票务
            ArrayList<ProductTicket> productTickets = new ArrayList<>();
            for (SeatDto seatDto : seatDtos) {
                ProductTicket productTicket = new ProductTicket();
                productTicket.setSessionId(session.getSessionId());
                productTicket.setSeatId(seatDto.getSeatId());
                productTicket.setSeatRow(seatDto.getSeatRow());
                productTicket.setSeatCol(seatDto.getSeatCol());
                productTicket.setTicketState(StateData.TICKET_STATE_NORMAL);
                productTickets.add(productTicket);
            }
            productTicketService.saveBatch(productTickets);
        }
        return true;
    }


    /**
     * 批量排场次（老师）
     * @param forms
     * @return
     */
    @Override
    @Transactional
    public Boolean createsList(List<CreateSessionForm> forms){
        //前置判断
        //参数自己的时间冲突判断
        for (int i = 0; i < forms.size(); i++) {
            CreateSessionForm csf = forms.get(i);
            for (int j = 0; j < forms.size(); j++) {
                if (i==j) {
                    continue;
                }
                if (!Objects.equals(forms.get(i).getHallId(),forms.get(j).getHallId())) {
                    continue;
                }
                CreateSessionForm nextCsf = forms.get(j);
                Long start = DateUtil.parse(csf.getSessionStartTime(), "yyyy-MM-dd HH:mm:ss").getTime();
                Long end = DateUtil.parse(csf.getSessionEndTime(), "yyyy-MM-dd HH:mm:ss").getTime();
                Long nextStart = DateUtil.parse(nextCsf.getSessionStartTime(), "yyyy-MM-dd HH:mm:ss").getTime();
                Long nextEnd = DateUtil.parse(nextCsf.getSessionEndTime(), "yyyy-MM-dd HH:mm:ss").getTime();
                if (!((start<nextStart&&end<nextStart)||(start>nextEnd&&end>nextEnd))) {
                    throw new ProductException(2001,"场次时间有冲突");
                }
            }
        }
        //收集相关数据做判定
        HashSet<Integer> movieIds = new HashSet<>();
        HashSet<Integer> hallIds = new HashSet<>();
        HashSet<Integer> seatIds = new HashSet<>();
        for (CreateSessionForm form : forms) {
            movieIds.add(form.getMovieId());
            hallIds.add(form.getHallId());
            seatIds.addAll(form.getSeatsIds());
        }
        //查询电影
        List<ProductMovie> productMovies = productMovieService.listByIds(movieIds);
        HashMap<Integer, ProductMovie> mapMovies = new HashMap<>();
        for (ProductMovie productMovie : productMovies) {
            if (productMovie.getMovieState()!= StateData.MOVIE_STATE_NORMAL) {
                throw new ProductException(2002,productMovie.getMovieName()+"没有上架");
            }
            mapMovies.put(productMovie.getMovieId(),productMovie);
        }

        //查询影厅
        Result<List<HallDto>> hallResult = cinemaClient.selHallIds(hallIds);
        if (hallResult.getCode()!=200) {
            throw new ProductException(hallResult.getCode(),hallResult.getMsg());
        }
        List<HallDto> hallDtos = hallResult.getData();
        if (hallDtos==null||hallDtos.size()<=0||hallDtos.size()!=hallIds.size()) {
            throw new ProductException(2003,"有影厅不存在");
        }
        Map<Integer, HallDto> mapHds = new HashMap<>();
        for (HallDto hallDto : hallDtos) {
            if (hallDto.getHallState()!= StateData.HALL_STATE_NORMAL) {
                throw new ProductException(2004,"有影厅不可用");
            }
            mapHds.put(hallDto.getHallId(),hallDto);
        }
        //查询座位
        Result<List<SeatDto>> seatResult = new Result<List<SeatDto>>();
        HashMap<Integer, Set<Integer>> hallSeats = new HashMap<>();
        if (seatIds!=null&&seatIds.size()>0){
            seatResult= cinemaClient.byNormalSeatIds(seatIds);
            if (seatResult.getCode()!=200) {
                throw new ProductException(seatResult.getCode(),seatResult.getMsg());
            }
            List<SeatDto> seatDtos = seatResult.getData();
            if (seatDtos==null||seatDtos.size()<=0||seatDtos.size()!=seatIds.size()) {
                throw new ProductException(2005,"有座位不存在");
            }
            //获取影厅座位关系
            for (SeatDto seatDto : seatDtos) {
                Set<Integer> sets = hallSeats.get(seatDto.getHallId());
                if (sets==null) {
                    sets = new HashSet<>();
                    hallSeats.put(seatDto.getHallId(),sets);
                }
                sets.add(seatDto.getSeatId());
            }
        }
        //检查每个场次参数逻辑的正确性
        for (CreateSessionForm form : forms) {
            Long start = DateUtil.parse(form.getSessionStartTime(), "yyyy-MM-dd HH:mm:ss").getTime();
            Long end = DateUtil.parse(form.getSessionEndTime(), "yyyy-MM-dd HH:mm:ss").getTime();
            ProductMovie pm = mapMovies.get(form.getMovieId());
            Long time = pm.getMovieTime()*60*1000L;
            if (end-start<time) {
                throw new ProductException(2007,pm.getMovieName()+"排场时间有不足");
            }
            //座位与影厅是否关系正确
            if (form.getSeatsIds()!=null&&form.getSeatsIds().size()>0) {
                Set<Integer> sts = hallSeats.get(form.getHallId());
                if (!(sts.size()==form.getSeatsIds().size()&&sts.containsAll(form.getSeatsIds()))) {
                    throw new ProductException(2008,"有座位与影厅不匹配");
                }
            }
        }
        //查询所有的上架的场次信息
        List<ProductSession> productSessions = this.list(new QueryWrapper<ProductSession>().eq("session_state", StateData.SESSION_STATE_NORMAL));
        List<ProductSession> saveSessions = new ArrayList<>();
        HashMap<String, CreateSessionForm> maps = new HashMap<>();
        for (CreateSessionForm form : forms) {
            Long start = DateUtil.parse(form.getSessionStartTime(), "yyyy-MM-dd HH:mm:ss").getTime();
            Long end = DateUtil.parse(form.getSessionEndTime(), "yyyy-MM-dd HH:mm:ss").getTime();
            for (ProductSession ps : productSessions) {
                if (ps.getHallId()!=form.getHallId()) {
                    continue;
                }
                Long nextStart = DateUtil.parse(ps.getSessionStartTime(), "yyyy-MM-dd HH:mm:ss").getTime();
                Long nextEnd = DateUtil.parse(ps.getSessionEndTime(), "yyyy-MM-dd HH:mm:ss").getTime();
                if (!((start<nextStart&&end<nextStart)||(start>nextEnd&&end>nextEnd))) {
                    throw new ProductException(2001,"场次时间有冲突");
                }
            }
            ProductSession ps = BeanUtil.toBean(form, ProductSession.class);
            HallDto hd = mapHds.get(form.getHallId());
            ProductMovie pm = mapMovies.get(form.getMovieId());
            ps.setHallName(hd.getHallName());
            ps.setMovieImage(pm.getMovieImage());
            ps.setMovieName(pm.getMovieName());
            ps.setMovieTime(pm.getMovieTime());
            ps.setSessionState(StateData.SESSION_STATE_NORMAL);
            saveSessions.add(ps);
            maps.put(ps.getHallId()+ps.getSessionStartTime(),form);
        }
        //排场数据写入
        this.saveBatch(saveSessions);
        List<ProductTicket> pts = new ArrayList<>();
        for (ProductSession ps : saveSessions) {
            pts.clear();
            CreateSessionForm csf = maps.get(ps.getHallId() + ps.getSessionStartTime());
            Set<Integer> seatsIds = csf.getSeatsIds();
            //当输入的seatsIds为空或者为0时全查
            if (seatsIds==null||seatsIds.size()<=0) {
                Result<List<SeatDto>> seatsResult = cinemaClient.selSeatNormal(csf.getHallId());
                if (seatsResult.getCode()!=200) {
                    throw new ProductException(seatsResult.getCode(),seatsResult.getMsg());
                }
                for (SeatDto sd : seatsResult.getData()) {
                    ProductTicket pt = new ProductTicket();
                    pt.setSeatCol(sd.getSeatCol());
                    pt.setSeatId(sd.getSeatId());
                    pt.setSeatRow(sd.getSeatRow());
                    pt.setSessionId(ps.getSessionId());
                    pt.setTicketState(StateData.TICKET_STATE_NORMAL);
                    pts.add(pt);
                }
            }else {//当不为空时，则按照传入的seatsID去生成票务信息
                for (Integer seatId : seatIds) {
                    for (SeatDto sd : seatResult.getData()) {
                        if (seatId==sd.getSeatId()) {
                            ProductTicket pt = new ProductTicket();
                            pt.setSeatCol(sd.getSeatCol());
                            pt.setSeatId(sd.getSeatId());
                            pt.setSeatRow(sd.getSeatRow());
                            pt.setSessionId(ps.getSessionId());
                            pt.setTicketState(StateData.TICKET_STATE_NORMAL);
                            pts.add(pt);
                        }
                    }
                }
            }
            //保存座位
            productTicketService.saveBatch(pts);
        }
        return true;
    }
}
